package edu.ccut.saturn.component.impl.node;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import edu.ccut.saturn.component.ComponentFactory;
import edu.ccut.saturn.component.ComponentManager;
import edu.ccut.saturn.component.IComponent;
import edu.ccut.saturn.component.ILogicContext;
import edu.ccut.saturn.component.IParameterContext;
import edu.ccut.saturn.component.IParameterInfo;
import edu.ccut.saturn.component.impl.SaturnActionContext;
import edu.ccut.saturn.component.loader.node.NodeInfoLoader;
import edu.ccut.saturn.component.loader.node.SubActionComponentNodeInfoLoader;
import edu.ccut.saturn.component.xml.ISaturnElement;
import edu.ccut.saturn.component.xml.SaturnElement;

/**
 * action组件中的action结点，表示对另一个展现组件的引用。 <code>SubActionNodeInfo</code>扩展了<code>AbstractNodeInfo</code>,字段key
 * 记录了引用的action组件的键值。
 * 
 * @author 温建
 * 
 * @version 1.0 2008.8.20
 * 
 * @see AbstractNodeInfo
 * @see ILogicContext
 * @see IParameterContext
 * 
 */
public class SubActionNodeInfo extends AbstractNodeInfo {

	private String key;

	/**
	 * 获得引用的子action组件的键值
	 * 
	 * @return
	 */
	public String getKey() {
		return key;
	}

	/**
	 * 设置引用的子action组件的键值
	 * 
	 * @param key
	 */
	public void setKey(String key) {
		this.key = key;
	}

	public SubActionNodeInfo(String id, String name, String description,
			String nextId, List<IParameterInfo> parameters, String key,
			String x, String y) {

		super(id, name, description, nextId, null, x, y, "sub-action");
		this.key = key;
	}

	/**
	 * 子action结点的执行逻辑。首先根据键值获得组件实例；然后根据当前上下文中记录的
	 * request和response对象生成参数上下文；最后调用子action组件实例的执行方法。
	 * TODO:如何处理子action结点执行结束后，需要返回到父action流程继续运行后继结点的情况。
	 * 当前解决方法是当子action运行过程中发现没有下一个可运行结点时就返回到父action组件，并
	 * 继续执行子action结点后面的结点，共享request、response对象。
	 * 
	 * @param logicContext
	 *            逻辑上下文对象，记录结点运行时的上下文信息
	 * @throws Exception
	 */
	public void doExecute(ILogicContext logicContext) throws Exception {

		IParameterContext parameterContext = this
				.createParameterContext(logicContext);

		try {
			IComponent subAction = ComponentManager.getInstance().getComponent(
					this.key);

			ComponentManager.beforeExecuteComponent(this, parameterContext);
			subAction.execute(parameterContext);

		} finally {
			ComponentManager.afterExecuteComponent(this, parameterContext);
		}
	}

	@Override
	protected IParameterContext createParameterContext(
			ILogicContext logicContext) throws Exception {

		IParameterContext parameterContext = ComponentFactory
				.createParameterContext();

		parameterContext.addParameter(((SaturnActionContext) logicContext)
				.getRequest(), HttpServletRequest.class);

		parameterContext.addParameter(((SaturnActionContext) logicContext)
				.getResponse(), HttpServletResponse.class);

		return parameterContext;
	}

	public ISaturnElement toSaturnElement() {
		ISaturnElement subActionElement = new SaturnElement(
				SubActionComponentNodeInfoLoader.KEY_NODETYPE_ACTION);

		subActionElement.addAttribute(NodeInfoLoader.KEY_NODE_ID, this.getId());
		subActionElement.addAttribute(NodeInfoLoader.KEY_NODE_NEXT, this
				.getNextId());

		subActionElement.addAttribute(NodeInfoLoader.KEY_NODE_X, this.getX());
		subActionElement.addAttribute(NodeInfoLoader.KEY_NODE_Y, this.getY());

		ISaturnElement keyElement = new SaturnElement(NodeInfoLoader.KEY_NODE_KEY,
				this.getKey());

		ISaturnElement nameElement = new SaturnElement(
				NodeInfoLoader.KEY_NODE_NAME, this.getName());

		ISaturnElement descriptionElement = new SaturnElement(
				NodeInfoLoader.KEY_NODE_DESCRIPTION, this.getDescription());

		ISaturnElement parametersElement = new SaturnElement(
				NodeInfoLoader.KEY_PARAMETER_LIST);

		if (this.getParameterInfos() != null) {

			for (IParameterInfo parameterInfo : this.getParameterInfos()) {
				parametersElement.addSubElement(parameterInfo.toSaturnElement());
			}
		}
		subActionElement.addSubElement(keyElement);
		subActionElement.addSubElement(nameElement);
		subActionElement.addSubElement(descriptionElement);
		subActionElement.addSubElement(parametersElement);
		return subActionElement;
	}
}
